/******************************************************************************
 * Copyright (C) 2024 Createtree, Inc.(Gmbh) or its affiliates.
 *
 * All Rights Reserved.
 *
 * @file bsp_led_handler.c
 *
 * @par dependencies
 * - bsp_led_handler.h
 * - stdint.h
 *
 * @author Createtree | R&D Dept. | liuzhihuawy@163.com
 *
 * @brief Provide the APP APIs of LED and corresponding opetions.
 *
 * Processing flow:
 *
 * call directly.
 *
 * @version V1.0 2024-10-27
 *
 * @note 1 tab == 4 spaces!
 *
 *****************************************************************************/

//***************************** Includes begin ******************************//
#include "bsp_led_handler.h"
//***************************** Includes end ********************************//

//***************************** Defines begin *******************************//

typedef struct
{
    uint16_t              period;
    uint8_t          blink_times;
    led_duty_cycle_t  duty_cycle;
    led_id_t              led_id;
} led_event_t;

#ifdef DEBUG
/* User provide the LED_LOG at other files */
extern int LED_LOG(const char * fmt, ...);
#else
#define LED_LOG(fmt, ...)
#endif /* DEBUG */

#ifdef USING_LED_ASSERT
#ifdef DEBUG
static void LED_ASSERT(int LineNum, const char * FuncName)
{
    LED_LOG("Assert failed in line:%d,func:%s\r\n", LineNum, FuncName);
    while (1)
    {
        ;
    }
}
#define ASSERT(condition) if(!(condition)){LED_ASSERT(__LINE__, __func__);}
#else 
#error "USING_LED_ASSERT is defined, but DEBUG is not defined!"
#endif /* DEBUG */
#else
#define ASSERT(condition)
#endif /* USING_LED_ASSERT */

/**
 * @brief init the led instances array
 * @param arr  led instances array
 * @param size array size
 * @retval led_handler_status_t 
 * @date 2024-10-27
 */
static led_handler_status_t __array_init(bsp_led_driver_t * arr[],
                                         uint32_t size)
{
    for (size_t i = 0; i < size; i++)
    {
        arr[i] = HANDLER_INSTANCE_PATTERN;
    }
    return LED_HANDLER_STATUS_OK;
}

/**
 * @brief led handler task
 * @param arg handler self pointer
 * @date 2024-10-30
 */
static void led_handler_thread(void const *arg)
{
    /* Check the arg is not NULL */
    ASSERT(NULL != arg);
    bsp_led_handler_t * const self = (bsp_led_handler_t *)arg;
    led_event_t              event;
    led_handler_status_t    status;
    bsp_led_driver_t          *dev;
    /* Check the handler is inited */
    if (self->is_inited != LED_HANDLER_INITED)
        self->p_os_sleep->pf_sleep(1);
    LED_LOG("led_handler_thread start\r\n");
    while (1)
    {
        /* wait for event */
        status = self->p_os_queue_op->pf_os_queue_pop(self->queue_handle,
                                                      &event,
                                                      1000);
        while (LED_HANDLER_STATUS_OK == status)
        {
            LED_LOG("led_handler_thread event:\r\n");
            LED_LOG("    led_id:     %d\r\n", event.led_id);
            LED_LOG("    period:     %d\r\n", event.period);
            LED_LOG("    blink_times:%d\r\n", event.blink_times);
            LED_LOG("    duty_cycle: %d\r\n", event.duty_cycle);
            /* Check led_id */
            if (event.led_id >= BSP_ID_LED_MAX)
            {
                LED_LOG("led_id error\r\n");
                break;
            }
            /* Check the led driver is registered */
            dev = self->instances.group[event.led_id];
            if (HANDLER_INSTANCE_PATTERN == dev)
            {
                LED_LOG("led driver is not registered\r\n");
                break;
            }
            /* Call the led driver to control the led */
            dev->pf_led_ctrl(dev,
                             event.period,
                             event.blink_times,
                             event.duty_cycle);
            break;
        }
    }
}

/**
 * @brief register the led driver to the led handler
 * @param self  handler self pointer
 * @param hled_drv led driver pointer
 * @param led_id used to store the returned id
 * @retval led_handler_status_t 
 * @date 2024-10-30
 */
static led_handler_status_t handler_led_register (
                             bsp_led_handler_t            *self,
                             bsp_led_driver_t * const  hled_drv,
                             led_id_t         * const     led_id
                             )
{
    led_handler_status_t ret = LED_HANDLER_STATUS_OK;
/************* 1. Check the input parameters *************/
    ASSERT(NULL != self);
    ASSERT(NULL != hled_drv);
    ASSERT(NULL != led_id);
    LED_LOG("handler_led_register start\r\n");
    do {
/********** 2. Check the led handler is inited ***********/
        if (LED_HANDLER_INITED != self->is_inited ||
            LED_DRIVER_INITED  != hled_drv->is_inited)
        {
            ret = LED_HANDLER_STATUS_NOT_INITED;
            break;
        }
/******** 3. Check if the led driver is registered *******/
        for (size_t i = 0; i < self->instances.num; i++)
        {
            if (hled_drv == self->instances.group[i])
            {
                ret = LED_HANDLER_STATUS_REGISTERED;
                break;
            }
        }
/*************** 4. Register the led driver **************/
#ifdef OS_SUPPORTING
        self->p_os_critical->pf_os_critical_enter();
#endif /* OS_SUPPORTING */
        if (self->instances.num < MAX_LED_INSTANCE_NUM)
        {
            *led_id = (led_id_t)self->instances.num;
            self->instances.group[self->instances.num++] = hled_drv;
        }
#ifdef OS_SUPPORTING
        self->p_os_critical->pf_os_critical_exit();
#endif /* OS_SUPPORTING */
        ASSERT(self->instances.num < MAX_LED_INSTANCE_NUM);
        LED_LOG("led register success\r\n");
    } while(0);
    LED_LOG("handler_led_register end\r\n");
    return ret;
}

/**
 * @brief led control function of handler
 * @param self bsp_led_handler_t instance
 * @param period led blink period [ms]
 * @param blink_times led blink times [1-255]
 * @param duty_cycle  led duty cycle [led_duty_cycle_t]
 * @param led_id led id [led_id_t]
 * @retval led_handler_status_t 
 * @note control the led
 * 
 * @date 2024-10-27
 */
static led_handler_status_t handler_led_ctrl (
                             bsp_led_handler_t  * const    self,
                             const uint16_t              period,  /* ms    */
                             const uint8_t          blink_times,  /* times */
                             const led_duty_cycle_t  duty_cycle,
                             const led_id_t               led_id
                             )
{
    led_handler_status_t ret = LED_HANDLER_STATUS_OK;
/************* 1. Check the input parameters *************/
    ASSERT(NULL != self);
    ASSERT(period > 0  && period < 10E3);
    ASSERT(blink_times > 0 && blink_times <= 255);
    ASSERT((int)duty_cycle >= LED_DUTY_CYCLE_MIN &&
           (int)duty_cycle <= LED_DUTY_CYCLE_MAX);
    ASSERT((int)led_id     <  BSP_ID_LED_MAX);
    LED_LOG("led handler control start\r\n");
    do {
/*********** 2. Check the led handler is inited **********/
        if (LED_HANDLER_INITED != self->is_inited)
        {
            ret = LED_HANDLER_STATUS_NOT_INITED;
            LED_LOG("led handler is not inited\r\n");
            break;
        }
/**** 3. send a massage to the led driver to control *****/
        led_event_t led_event = {
            .period      =      period,
            .blink_times = blink_times,
            .duty_cycle  =  duty_cycle,
            .led_id      =       led_id
        };
        ret = self->p_os_queue_op->pf_os_queue_push(self->queue_handle,
                                                    &led_event,
                                                    0);
        if (LED_HANDLER_STATUS_OK != ret)
        {
            LED_LOG("send led event failed, ret:%d\r\n", ret);
            break;
        }
        LED_LOG("send led event success\r\n");
    } while(0);
    LED_LOG("led handler control end\r\n");
    return ret;
}

/**
 * 
 * @brief led constructor function
 * @param self led handler instance
 * @param hled_op led operations instance
 * @param hos_sleep os sleep instance
 * @param htime_base time base instance
 * @retval led_handler_status_t status of the operation
 * @note This function is used to initialize the led handler
 *  steps:
 *  1. Check the input parameters
 *  2. Check if the led handler is already initialized
 *  3. Add the interfaces to the led handler instance
 *  4. Initialize the led handler
 * @date 2024-10-27
 */
led_handler_status_t led_handler_constructor(
                            bsp_led_handler_t        * const             self,
#ifdef OS_SUPPORTING
                      const handler_os_sleep_t       * const       p_os_sleep,
                      const handler_os_queue_op_t    * const    p_os_queue_op,
                      const handler_os_critial_t     * const    p_os_critical,
                      const handler_os_thread_op_t   * const   p_os_thread_op,
#endif /* OS_SUPPORTING */
                      const handler_time_base_t      * const      p_time_base)
{
    led_handler_status_t ret = LED_HANDLER_STATUS_OK;
/************* 1. Check the input parameters *************/
    ASSERT(NULL != self           &&
#ifdef OS_SUPPORTING
           NULL != p_os_sleep     &&
           NULL != p_os_queue_op  &&
           NULL != p_os_critical  &&
           NULL != p_os_thread_op &&
#endif /* OS_SUPPORTING */
           NULL != p_time_base       );

#ifdef OS_SUPPORTING
    ASSERT(NULL != p_os_sleep->pf_sleep);

    ASSERT(NULL != p_os_queue_op->pf_os_queue_create);
    ASSERT(NULL != p_os_queue_op->pf_os_queue_push);
    ASSERT(NULL != p_os_queue_op->pf_os_queue_pop);
    ASSERT(NULL != p_os_queue_op->pf_os_queue_delete);

    ASSERT(NULL != p_os_critical->pf_os_critical_enter);
    ASSERT(NULL != p_os_critical->pf_os_critical_exit);

    ASSERT(NULL != p_os_thread_op->pf_os_thread_create);
    ASSERT(NULL != p_os_thread_op->pf_os_thread_delete);
#endif /* OS_SUPPORTING */
    ASSERT( NULL != p_time_base->pf_get_time);

    do {
        LED_LOG("led handler constructor start\r\n");

/*** 2. Check if the led handler is already initialized **/
        if (LED_HANDLER_INITED == self->is_inited)
        {
            ret = LED_HANDLER_STATUS_INITED;
            LED_LOG("led handler was inited\r\n");
            break;
        }

/*** 3. Add the interfaces to the led handler instance ***/
        LED_LOG("adding the interfaces\r\n");
        // 3.1 mount external interfaces ------------------
#ifdef OS_SUPPORTING
        self->p_os_sleep            =           p_os_sleep;
        self->p_os_queue_op         =        p_os_queue_op;
        self->p_os_critical         =        p_os_critical;
        self->p_os_thread_op        =       p_os_thread_op;
#endif /* OS_SUPPORTING */
        self->p_time_base           =          p_time_base;
        // 3.2 mount internal interfaces ------------------
        self->pf_led_ctrl = handler_led_ctrl;
        self->pf_led_register = handler_led_register;
/************* 4. Initialize the led handler *************/
        // 4.1 initialize the os related interfaces -------
#ifdef OS_SUPPORTING
        // 4.2 create new thread
        self->p_os_thread_op->pf_os_thread_create( led_handler_thread,
                                                   "led_handler",
                                                   256,
                                                   self,
                                                   0,
                                                   &self->thread_handle );
        if (LED_HANDLER_STATUS_OK != ret)
        {
            LED_LOG("create thread failed\r\n");
            break;
        }
        ASSERT(NULL != self->thread_handle);
        // 4.3 create new queue
        ret = self->p_os_queue_op->pf_os_queue_create( MAX_LED_INSTANCE_NUM,
                                                       sizeof(led_event_t),
                                                       &self->queue_handle);
        if (LED_HANDLER_STATUS_OK != ret)
        {
            LED_LOG("create queue failed\r\n");
            self->p_os_thread_op->pf_os_thread_delete(self->thread_handle);
            break;
        }
        ASSERT(NULL != self->queue_handle);
#endif /* OS_SUPPORTING */
        // 4.4 init internal variables
        self->instances.num = 0;
        ret = __array_init(&self->instances.group[0], MAX_LED_INSTANCE_NUM);
        if (LED_HANDLER_STATUS_OK != ret)
        {
            LED_LOG("init array failed\r\n");
#ifdef OS_SUPPORTING
            self->p_os_queue_op->pf_os_queue_delete(self->queue_handle);
            self->p_os_thread_op->pf_os_thread_delete(self->thread_handle);
#endif /* OS_SUPPORTING */
            break;
        }
        self->is_inited = LED_HANDLER_INITED;
        LED_LOG("led handler constructor end\r\n");
    } while (0);
    return ret;
}

//******************************* Defines end *******************************//

//****************************** Declaring begin ****************************//

//****************************** Declaring end ******************************//
